package test;


import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.openal.SoundStore;
import soundly.Mix;
import soundly.SoundGroup;
import soundly.Soundly;
import soundly.XSound;
import soundly.XStreamingSound;

//TODO: add update() for SoundManager, and allow for things like:
//   delayed sounds
//   auto-updating sounds (e.g. ambient bird chirps every 10 seconds)

/**
 *
 * @author Matt
 */
public class ScrollerSound {
    
    private Scroller scroller;
    
    //footstep materials
    SoundGroup footsteps;
    XSound gunshot;
    XSound casings;
    XSound speech;
    
    //initially at a quarter volume
    final Mix PLAYER_MIX = new Mix(0.5f);
    final Mix FOOTSTEPS_MIX = new Mix(0.5f);
    
    public static final float MIX_ADJ = .0005f;
    public static final float MIX_MAX = 1f;
    
    private long shotTime = 1000;
    private long lastShotTime = 0;
    
    private long stepTime = 350;
    private long lastStepTime = 0;
    private long shotRenderTime = 400;
    private boolean shot = false;
    
    private Soundly sound;
    
    public static final int LAYER_AMBIENCE = 0;
    public static final int LAYER_FOLEY = 1;
    public static final int LAYER_SFX = 2;
    public static final int LAYER_SPEECH = 3;
    
    private int ground = Scroller.GRASS;
    
    Color listener = new Color(1f, 0f, 0f, 0.25f);
    Color sfx = new Color(1f, 1f, 1f, 0.25f);
    
    public static final float TILE = 32f;
    
    Image speechChar;
    
    private XSound music;
    
    private boolean showHelp = true;
    
    
    
    private Color bg = new Color(0f, 0f, 0f, 0.25f);
    private Color step = new Color(1f, 1f, 1f, 0.5f);
    
    //TODO: Occasional sounds... implement Timer within sound e.g. sound.setDelay(5000); sound.setRepeat(true);
    
    public ScrollerSound(Scroller scroller) {
        this.scroller = scroller;
    }
    
        //Check to see if a source is available. 
        //If not, determine whether the desired SFX is of higher priority 
            //If so, kick out the lowest priority SFX and replace with higher priority
            //If not, do not play SFX
        //If sound exists outside of "area of play" (by default, a function which returns the screen)
    
    public void init(GameContainer container) throws SlickException {
        sound = Soundly.get();
        
        //set up the sound with a max of 4 sources (for demonstration)
        sound.init(4);
        
        //we want the radius of the player (i.e. listener) to be relatively small
        sound.setListenerRadius(30f);
        
        //In our example we want to use proximity like so:
           //When the player is within earshot of the british girl, make sure she
           //is heard. Otherwise, other sounds can override her.
        //Distance checking (off by default) allows a less-important sound to override
        //a more important but 'out of earshot' sound.
        sound.setDistanceCheckingEnabled(true);
        
        //To enable a better sense of proximity, we will turn this on. When we enter
        //the British Girl's radius, her voice will be heard at the lowest volume,
        //and it will increase in a linear fashion as we get closer to her.
        //Note: By default, proximity is ON for SoundManager (i.e. it is the "global" proximity)
        sound.setProximityEnabled(true); 
        
        //Let's include a sprite for our sound girl
        speechChar = new Image("res/girl.png");
        
        //The "speech" sound. Point to a file and give it a description...
        speech = new XSound("res/speech.ogg", "British Girl");
        //Set the layer priority. A playing sound will not be overridden
        //by anything lower than it (except for certain cases, such as distance checking).
        speech.setLayer(LAYER_SPEECH);
        
        //Give our speech girl a large radius that we can "hear" within
        speech.setRadius(300f);
        speech.setPosition(150, 150);
        
        //By default, proximity is OFF for Sound2D
        speech.setProximityEnabled(true);
        
        //And ensure that she is looped when played
        speech.setLooping(true);
        
        //Let's use some groups to create a footstep sound effect
        footsteps = new SoundGroup("footsteps", 2);
        
        //When the character steps on grass, we'll use this group...
        SoundGroup grass = new SoundGroup(5);
        grass.addSound(new XSound("res/footsteps/grass1.wav", "grass1"));
        grass.addSound(new XSound("res/footsteps/grass2.wav", "grass2"));
        grass.addSound(new XSound("res/footsteps/grass3.wav", "grass3"));
        grass.addSound(new XSound("res/footsteps/grass4.wav", "grass4"));
        grass.addSound(new XSound("res/footsteps/grass5.wav", "grass5"));
        
        //When the character steps on the wooden bridge, we'll use this group
        SoundGroup wood = new SoundGroup(4);
        wood.addSound(new XSound("res/footsteps/wood1.wav", "wood1"));
        wood.addSound(new XSound("res/footsteps/wood2.wav", "wood2"));
        wood.addSound(new XSound("res/footsteps/wood3.wav", "wood3"));
        wood.addSound(new XSound("res/footsteps/wood4.wav", "wood4"));
        
        //Put the different steps into the footsteps group...
        footsteps.addSound(grass);
        footsteps.addSound(wood);
        
        //Set up a simple mix so it all sounds good to start.
        wood.setVolume(1f);
        grass.setVolume(0.8f);
        FOOTSTEPS_MIX.setVolume(0.5f);
        footsteps.setMix(FOOTSTEPS_MIX);
        
        //We'll lower the layer to AMBIENCE, the lowest we've defined
        footsteps.setLayer(LAYER_AMBIENCE); 
        
        //This will disable the 3D effects and have the footsteps play front and
        //center
        footsteps.setRelative(true);
        
        //Create a gunshot sound effect
        gunshot = new XSound("res/gun.ogg", "gunshot");
        gunshot.setRelative(true);
        gunshot.setLayer(LAYER_SFX);
        
        //Footsteps, gunshots and casings will use this mix; allowing us to control
        //the "player" sounds without having to adjust each individually. 
        //Player mix will start at 
        gunshot.setMix(PLAYER_MIX);
        
        //Fine-tune the priority to ensure that 'casings' will not override it
        gunshot.setPriority(0.5f);
        
        casings = new XSound("res/casings.ogg", "casings");
        casings.setRelative(true);
        casings.setLayer(LAYER_SFX);
        casings.setMix(PLAYER_MIX);
        casings.setVolume(0.30f); //we want it a bit quieter; let's say 60% vol
        
        //By default, Soundly reserves the first source for music and similarly
        //important sounds. We can make room for more music tracks (or take them
        //out completely) with Soundly's setMusicTrackCount method.
        music = new XStreamingSound("res/ambience2.ogg", "ambient");
        music.setMusic(true);
        music.setVolume(0f);
        music.setLooping(true);
        music.fade(1000, 1f, false);
        
        //Since setMusicTrack is true, this will try to play the sound in the
        //first available MUSIC source (by default, the source at index 0 is
        //the only music track).
        music.queue();
        
        //debug source info...
        SourceDebugOverlay.init(container);
    }
    
    public void updateFootsteps(int delta) {
        //ever time we get a step, let's update...
        if (System.currentTimeMillis()-lastStepTime > stepTime) {
            float pitch = 1.0f + (((float)Math.random() * .5f) - ((float)Math.random() * .5f));
            float vol = ((float)Math.random() * 0.2f);
            
            int g = ground==Scroller.WOOD ? 1 : 0;
            
            //Randomize the playback volume and mix it with the PLAYER_MIX
            footsteps.setVolume(vol * PLAYER_MIX.getVolume());
            footsteps.setPitch(pitch);
            footsteps.queueSound(g);
            
            lastStepTime = System.currentTimeMillis();
        }
    }
    
    
    public void update(GameContainer container, int delta) throws SlickException {
        ground = scroller.groundAt(scroller.playerX, scroller.playerY);
        
        //update the listener
        sound.setListenerPosition(scroller.playerX*32f, scroller.playerY*32f);
        
        if (scroller.movement == scroller.WALK) {
            updateFootsteps(delta);
        }
        
        if (container.getInput().isKeyDown(Input.KEY_SPACE)) {
            if (!shot) {
                shot = true;
                scroller.shooting = true;
                
                gunshot.queue();
                
                casings.setPitch(ground==Scroller.GRASS ? 0.8f : 1f);
                casings.queue();
                
                lastShotTime = System.currentTimeMillis();
            }
        }
        
        long current = System.currentTimeMillis();
        if (shot && current-lastShotTime > shotTime) {
            //we shot once... so only play it once before the time runs out
            shot = false;
        }
        if (current-lastShotTime > shotRenderTime) {
            scroller.shooting = false;
        }
        
        if (!speech.isPlaying() && speech.isWithinEarshot()) {
            speech.queue();
        }
        
        if (container.getInput().isKeyDown(Input.KEY_H)) {
            float d = Soundly.GLOBAL_MIX.getVolume() - MIX_ADJ;
            Soundly.GLOBAL_MIX.setVolume(Math.max(0, d));
        } else if (container.getInput().isKeyDown(Input.KEY_Y)) {
            float d = Soundly.GLOBAL_MIX.getVolume() + MIX_ADJ;
            Soundly.GLOBAL_MIX.setVolume(Math.min(1f, d));
        } else if (container.getInput().isKeyDown(Input.KEY_J)) {
            float d = PLAYER_MIX.getVolume() - MIX_ADJ;
            PLAYER_MIX.setVolume(Math.max(0, d));
        } else if (container.getInput().isKeyDown(Input.KEY_U)) {
            float d = PLAYER_MIX.getVolume() + MIX_ADJ;
            PLAYER_MIX.setVolume(Math.min(1f, d));
        } else if (container.getInput().isKeyDown(Input.KEY_K)) {
            float d = FOOTSTEPS_MIX.getVolume() - MIX_ADJ;
            FOOTSTEPS_MIX.setVolume(Math.max(0, d));
        } else if (container.getInput().isKeyDown(Input.KEY_I)) {
            float d = FOOTSTEPS_MIX.getVolume() + MIX_ADJ;
            FOOTSTEPS_MIX.setVolume(Math.min(1f, d));
        }
        
        
        //update and play managed sounds
        sound.update(delta);
        
        //System.out.println(sound.isSourceRelative(sound.getSource(0)));
    }
    
    private float lastFadeout = 1f;
    
    
    public void renderInView(GameContainer container, Graphics g) throws SlickException {
        if (sound.isProximityEnabled()) {
            g.setColor(sfx);
            float rad = sound.getListenerRadius();
            g.fillOval(sound.getListenerX()-rad, sound.getListenerY()-rad, rad*2, rad*2);
            g.setColor(Color.white);
            //g.drawString("distance: "+speech.updateDistance(), sound.getX()+15, sound.getY()-10);
            //g.setColor(f);
            //rad = ground.getSoundRadius();
            //g.fillOval(metal.getX()*32-rad, metal.getY()*32-rad, rad*2, rad*2);

            g.setColor(sfx);
            rad = speech.getRadius();
            g.fillOval(speech.getX()-rad, speech.getY()-rad, rad*2, rad*2);
        }
        
        g.setColor(Color.white);
        g.drawString(speech.getDescription(), speech.getX()-35, speech.getY()-10);
        speechChar.draw(speech.getX(), speech.getY(), 0.35f);
        
    }
    
    public void render(GameContainer container, Graphics g) throws SlickException {
        SourceDebugOverlay.render(container, g);
        
        
        //g.fillOval(ground.getX()-ground.getSoundRadius(), ground.getY()-ground.getSoundRadius(), ground.getSoundRadius(), ground.getSoundRadius());
        
        
        if (showHelp) {
            g.setColor(Color.white);
            int h = container.getHeight();

            g.setColor(bg);
            g.fillRect(10, 395, 450, 200);

            String floor = "";
            if (ground==Scroller.GRASS) floor = "Grass";
            else if(ground==Scroller.WOOD) floor = "Wood";
            if (scroller.movement==Scroller.STEALTH) floor = "Stealth (muted)";
            g.setColor(step);
            g.drawString("Stepping on: "+floor, 15, h-25);
            g.setColor(Color.white);

            h -= 100;

            float barX = 220f;
            float barWidth = 100f;
            float barHeight = 15f;

            g.drawString("Actual Output", barX+barWidth+5, h+55);
            g.drawString("Volume", barX, h+55);

            //render text from bottom up...
            g.drawString("Footsteps Mix: ", 45, h+35);
            g.setColor(Color.red);
            g.fillRect(barX, h+35, (barWidth-5)*(FOOTSTEPS_MIX.getVolume()), barHeight);
            g.fillRect(barX+barWidth+5, h+35, (barWidth-5)*(PLAYER_MIX.getVolume()
                    *FOOTSTEPS_MIX.getVolume() 
                    *Soundly.GLOBAL_MIX.getVolume()), barHeight);

            g.setColor(Color.white);
            g.drawRect(barX, h+35, (barWidth-5), barHeight);
            g.drawRect(barX+barWidth+5, h+35, (barWidth-5), barHeight);

            g.drawString("Player Mix: ", 45, h+15);
            g.setColor(Color.red);
            g.fillRect(barX, h+15, (barWidth-5)*(PLAYER_MIX.getVolume()), barHeight);
            g.fillRect(barX+barWidth+5, h+15, (barWidth-5)*(PLAYER_MIX.getVolume()
                     *Soundly.GLOBAL_MIX.getVolume()), barHeight);

            g.setColor(Color.white);
            g.drawRect(barX, h+15, (barWidth-5), barHeight);
            g.drawRect(barX+barWidth+5, h+15, (barWidth-5), barHeight);

            g.drawString("Global Mix: ", 45, h-5);
            g.setColor(Color.red);
            g.fillRect(barX+barWidth+5, h-5, (barWidth-5)*(Soundly.GLOBAL_MIX.getVolume()), barHeight);
            g.setColor(Color.white);
            g.drawRect(barX+barWidth+5, h-5, (barWidth-5), barHeight);


            g.drawString("Press Y/H, U/J, and I/K to adjust the mix:", 15, h-25);

            g.drawString("SPACE to shoot", 15, h-45);

            String s = SoundStore.get().isMusicOn()?"(on)":"(off)";
            g.drawString("Press M to toggle music "+s, 15, h-65);
            g.drawString("WASD to move your player", 15, h-85);
            g.drawString("X to show/hide help", 15, h-105);
        }
    }
    
    public void keyPressed(int key, char c) {
        if (c=='m') {
            SoundStore.get().setMusicOn(!SoundStore.get().isMusicOn());
        } else if (c=='x') {
            showHelp = !showHelp;
        }
    }
}
